#include "STEClasses.h"
#include "Value.h"
#include "ParserUtil.h"
#include "Imm.h"
#include "AbsMachCode.h"

// Global Entry
void GlobalEntry::print(ostream& os, int indent) const {
	prtln(os,indent+STEP_INDENT);
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ((*it) && (*it)->name() != "any") {
				(*it)->print(os,indent+STEP_INDENT);
			}
		}
	}
	// Rules
	vector<RuleNode*>::const_iterator it = rules_.begin();
	for (;it!=rules_.end();++it) {
		if ((*it))
			(*it)->print(os,indent+STEP_INDENT);
	}
	prtln(os,indent);
	prtln(os,indent);
}

ImmCodes* GlobalEntry::code(bool controlFlow) {
	ImmCodes* c = NULL;
	if (symTab()) {
		c = new ImmCodes();
		c->add(c->newLabel("init_global"));
		SymTab::iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ((*it) && (*it)->kind() == VARIABLE_KIND) {
				c->add((*it)->code());
			}
		}
		// jump to event matcher
		c->add(ImmCode::GOTO,(void*)c->newLabel("S0"));
		//c->add(ImmCode::GOTO,(void*)c->newLabel("test"));
	}
	// print anythig other than vardecl
	if (symTab()) {
		SymTab::iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ((*it) && (*it)->kind() != VARIABLE_KIND &&
					(*it)->name() != "any") {
				c->add((*it)->code());
			}
		}
	}
	// Rules
	vector<RuleNode*>::const_iterator it = rules_.begin();
	for (;it!=rules_.end();++it) {
		if (c==NULL) c = new ImmCodes();
		if ((*it)) {
			c->add((*it)->code());
		}
	}
	return c;
}

// BlockEntry
void BlockEntry::print(ostream& out, int indent) const {
	// TODO
}

// Variable Entry
string VariableEntry::registerName() {
	if (reg_name.length()!=0) return reg_name;
	const Type* typ = type();
	// Figure out register name
	if (typ && typ->isFloat(typ->tag())) {
		// Allocate float register
		registerName(newRegister(true));
	} else {
		// Allocate integer register
		registerName(newRegister(false));
	}
	return reg_name;
}
void VariableEntry::print(ostream& os, int indent) const {
	string typeName = type()->name();
	if (type()->tag()==Type::CLASS) {
		typeName += " " + type()->typeDesc()->name();
	}
	if (initVal()) {
		os << typeName << " " << name() << " = ";
		initVal()->print(os,indent);
		os << ";";
	}
	else
		os << typeName << " " << name() << ";";
	prtln(os, indent);
}

ImmCodes* VariableEntry::code(bool controlFlow) {
	if (initVal()) {
		ImmCodes* c = new ImmCodes();
		c->add( initVal()->code() );
		RefExprNode* refNode = new RefExprNode(name(), this);
		c->add(ImmCode::EXPR, refNode, initVal()->addr(),NULL, OpNode::ASSIGN);
		return c;
	}
	return NULL;
}

// ClassEntry
void ClassEntry::print(ostream& out, int indent) const {
	out << "class " << name() << ";";
	prtln(out, indent);
}

// FunctionEntry

ImmCodes* FunctionEntry::code(bool controlFlow) {
	if (body()) {
		ImmCodes* c = new ImmCodes();
		// Insert function name label
		c->add(c->newLabel(name()));
		// Insert enter instruction
		c->add(ImmCode::ENTER, (void*)this);
		if (symTab()) {
			SymTab::iterator it = symTab()->begin();
			for (;it!=symTab()->end();++it) {
				if ( (*it) &&  (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
					VariableEntry* var = (VariableEntry*)(*it);
					if (var->varKind() != VariableEntry::PARAM_VAR) {
						c->add(var->code());
					}
				}
				//loop  invariant optimization
                if ( (*it) &&  (*it)->kind() == SymTabEntry::WHILE_LOOP_KIND && !hasRecursion()) {
                        ((WhileLoopEntry*)(*it))->doLoopInvariant();
                        //cout<<"here";
                        /*vector<StmtNode*>::iterator its = invariantlist->begin();
                        for(;its!=invariantlist->end();++its) {
                            (*its)->print(cout,0);
                            cout<<endl;
                            //c->add((*its)->code());
                        }*/
                }
			}
		}
		c->add(body()->code());
		/* Add default return statement */
		if (!body()->isLastStatementReturn())  {
			// add return only when last stmt is not return
			c->add(ImmCode::LEAVE, NULL, (void*)this );
		}
		return c;
	}
	return NULL;
}

void FunctionEntry::print(ostream& out, int indent) const {

	const Type* retT = type()->retType();
	retT->print(out,indent);
	out << " " << name() << "(";
	// Parameters
	string params;
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) && (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() == VariableEntry::PARAM_VAR) {
					if (params.empty()) params += var->type()->fullName() + " " + var->name();
					else params += ", " + var->type()->fullName()+ " " + var->name();
				}
			}
		}
	}
	out << params << ")";
	bool declExist = false;
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) && (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() != VariableEntry::PARAM_VAR) {
					declExist = true;
					break;
				}
			}
		}
	}
	if (declExist || body()) {
		out << " {";
		prtln(out,indent + STEP_INDENT);

		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) &&  (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() != VariableEntry::PARAM_VAR) {
					out  << var->type()->name() << " " << var->name() << ";";
					prtln(out,indent + STEP_INDENT);
				}
			}
		}
		if (body()) {
			body()->printWithoutBraces(out,indent+STEP_INDENT);
		}
		out << "};";
		prtln(out,indent);
	} else {
		out << ";";
		prtln(out,indent);
	}
}

// EventEntry
void EventEntry::print(ostream& out, int indent) const {
	const Type* t = type();
	out << "event " << name();

	if (symTab() == NULL) {
		out << ";";prtln(out,indent);
		return;
	} else {
		out << "(";
	}
	SymTab::const_iterator it = symTab()->begin();
	for (;it!=symTab()->end();) {
		if ((*it)) {
			(*it)->type()->print(out,indent);
			out << " " << (*it)->name();
		}
		if ( (++it) != symTab()->end() )
			out << ", ";
	}
	out << ");";
	prtln(out,indent);
}

//WhileLoopEntry
void WhileLoopEntry::print(ostream& out, int indent) const {}
